package com.yonder.test.waitnotify;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Box2 implements IBox {

    /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();
	
	private static final int getTime = 201;
	private static final int addTime = 200;

    /** Current number of elements */
    private final AtomicInteger count = new AtomicInteger();

	private Queue<String> productions = new ConcurrentLinkedQueue<String>();
	private int maxSize;
	
	public Box2(int maxSize) {
		super();
		this.maxSize = maxSize;
	}

    /**
     * Signals a waiting take. Called only from put/offer (which do not
     * otherwise ordinarily lock takeLock.)
     */
    private void signalNotEmpty() {
    	System.out.println("signalNotEmpty");
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
    }
    /**
     * Signals a waiting put. Called only from take/poll.
     */
    private void signalNotFull() {
    	System.out.println("signalNotFull");
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            notFull.signal();
        } finally {
            putLock.unlock();
        }
    }
    
	public String get(int id) {
		int c = 0;
		String production = null;
		try {
			this.takeLock.lockInterruptibly();
			while (productions.size() <= 0) {
				this.notEmpty.await();
			}
			Thread.sleep(getTime);
			production = productions.poll();
	        c = count.getAndDecrement();
			if (c > 1) {
				this.notEmpty.signal();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			this.takeLock.unlock();
		}
		if (c == maxSize) {
			signalNotFull();
		}
		return production;
	}
	
	public String add(String production, int id) {
        int c = -1;
		try {
			this.putLock.lockInterruptibly();
			while (productions.size() >= maxSize) {
				this.notFull.await();
			}
			Thread.sleep(addTime);
			productions.add(production);
            c = count.getAndIncrement();
            if (c + 1 < maxSize)
            	this.notFull.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			this.putLock.unlock();
		}
        if (c == 0)
            signalNotEmpty();
		return production;
	}
	
	
	public int getProductionCount() {
		return productions.size();
	}
	public int getMaxSize() {
		return maxSize;
	}
	public void setMaxSize(int maxSize) {
		this.maxSize = maxSize;
	}
}
